package model;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

public class IA extends Thread {

	private static final float MIN_SCORE = -99999;
	private Board board;
	private long startTime;

	private BrainMode brainMode;
	private BrainStatus brainStatus;
	private int depthLevel;
	private long thinkDelay;

	private Queue<Node> nodeQueue; // Nodes for every
															// legal move from
															// level 1 to N
	private ArrayList<Node> possibleNodes; // Nodes for
																	// possible
																	// moves

	/**
	 * 
	 * @param brainMode
	 * @param depthLevel
	 * @param timeLevel
	 *            in millisec
	 */
	public IA(BrainMode brainMode, int parameter) {
		// A.I. mode
		this.brainMode = brainMode;
		if (brainMode == BrainMode.DEPTH_MODE) {
			depthLevel = parameter;
			thinkDelay = -1;
		} else if (brainMode == BrainMode.TIME_MODE) {
			thinkDelay = parameter;
			depthLevel = -1;
		}
		this.brainStatus = BrainStatus.PAUSED;
		possibleNodes = new ArrayList<Node>();
		nodeQueue = new LinkedList<Node>();
		start();
	}

	public long getStartTime() {
		return startTime;
	}

	// TODO Debug this method
	public synchronized Move getBestMove() {
		Move bestMove = null;
		if (possibleNodes != null) {
			System.out.println("starting getBestMove *******");
			System.out.println("nb of nodes=" + possibleNodes.size());
			float bestScore = MIN_SCORE;
			float score;
			for (Node node : possibleNodes) {
				// TODO manage sign inversion here
				score = -node.getScore();
				if (bestScore < score) {
					bestScore = score;
					bestMove = node.getPreviousMove();
				}

				System.out.println("node: " + node.toString() + "  node move: "
						+ node.getPreviousMove().toString() + "  score="
						+ score);

			}
			if (bestMove != null) {
				System.out.println("FINAL - Best move: " + bestMove.toString()
						+ "  score=" + bestScore);
			} else
				System.out.println("FINAL - Best move is NULL !!!");
		}
		return bestMove;
	}

	public void setBoard(Board board) {
		possibleNodes.clear();
		nodeQueue.clear();
		// nodeStackIndex = 0;
		this.board = board;
		ArrayList<Move> possibleMoves = Engine.getLegalMoves(board);
		// adding root node
		for (Move move : possibleMoves) {
			Board childBoard = new Board(board);
			childBoard.Play(move);
			Node child = new Node(move, childBoard, 1);
			nodeQueue.offer(child);
			possibleNodes.add(child);
		}
	}

	@Override
	public void run() {
		super.run();
		while (true) {

			if (this.brainStatus == BrainStatus.THINKING) {
				int currentDepth = -1;
				// Thread main task
				while (((brainMode == BrainMode.TIME_MODE) && ((System.currentTimeMillis() - startTime) < thinkDelay))
						|| ((brainMode == BrainMode.DEPTH_MODE) && (currentDepth <= depthLevel))) {
						Node currentNode = nodeQueue.poll();
						if (currentNode != null) {
							currentDepth = currentNode.getDepth();
							if (!currentNode.isCreatedChildren()) {
								System.out
										.print("(+)" + currentNode.toString());
								currentNode.createChildren(nodeQueue);
								System.out.print(" => "
										+ currentNode.toString());
								System.out.println();
							}
						}
				}

				Move bestMove = getBestMove();
				if (bestMove != null) {
					board.Play(bestMove);
				}
				brainStatus = BrainStatus.PAUSED;
			}

			try {
				sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			;
		}
	}

	@Override
	public synchronized void start() {
		super.start();
	}

	public void setDepthLevel(int depthLevel) {
		this.depthLevel = depthLevel;
	}

	public void setTimeLevel(int timeLevel) {
		this.thinkDelay = timeLevel;
	}

	public void setDepthThinkMode() {
		brainMode = BrainMode.DEPTH_MODE;
	}

	public void setTimeThinkMode() {
		brainMode = BrainMode.TIME_MODE;
	}

	public void startThinking() {
		startTime = System.currentTimeMillis();
		brainStatus = BrainStatus.THINKING;
	}

}
